﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

using NHibernate;
using ProjectBase.Core;
using ProjectBase.Data;
using ProjectBase.Data.NH;

namespace $safeprojectname$
{
    public static partial class Ext
    {
        public static void Apply<TParent, TFilter>(this ICriteria criteria, TParent parentFilter, Expression<Func<TParent, TFilter>> expr)
            where TParent : IFilter<int>
            where TFilter : IFilter<int>
        {
            PublishingManager.Publish(null, () => "APPLY START", Severity.Debug);

            MemberExpression body;
            switch (expr.Body.NodeType)
            {
                case ExpressionType.MemberAccess: // e.g. entity.User
                    body = expr.Body as MemberExpression;
                    break;

                default:
                    throw new NotSupportedException(
                        "Provided 'Expression<Func<TParent, TFilter>> expr' parameter must be of a 'ExpressionType.MemberAccess'. ");
            }

            var compiled = GetCompiled(expr);

            var filter = compiled(parentFilter);

            if (filter.IsNull())
            {
                return;
            }

            var path = body.Member.Name;
            if (path.EndsWith("Filter"))
            {
                path = path.Remove((path.Length - 6));
            }

            var processor = ProjectBase.Core.Service.Factory.CreateInstance<IApplyFilterRestrictions<TFilter, int>>();

            var restrictions = processor.Apply(filter);

            if (restrictions.IsNotEmpty())
            {
                var persistentCriteria = criteria.CreateSubCriteria(path);
                persistentCriteria.Append(restrictions);
            }

            PublishingManager.Publish(null, () => "APPLY DONE", Severity.Debug);
        }

        private static Func<TParent, TFilter> GetCompiled<TParent, TFilter>(Expression<Func<TParent, TFilter>> expr)
            where TParent : IFilter<int>
            where TFilter : IFilter<int>
        {
            var key = typeof(TParent).Name + "|" + expr.ToString();
            object obj;
            if (!Funcs.TryGetValue(key, out obj))
            {
                lock (Locker)
                {
                    obj = expr.Compile();
                    Funcs[key] = obj;
                }
            }
            return obj as Func<TParent, TFilter>;
        }

        private static object Locker = new object();
        public static IDictionary<string, object> Funcs = new Dictionary<string, object>();
    }
}